﻿/**
  \file misc.c

  \author G. Icking-Konert

  \brief implementation of misc routines

  implementation of routines not really fitting anywhere else
*/


#include <string.h>
#include <stdlib.h>
// #include <unistd.h>
// #include <sys/time.h>

#include "misc.h"
#include "global.h"


// Windows specific
#if defined(WIN32) || defined(WIN64)

  #include "windows.h"

  // forground colours
  #define FG_BLACK      0
  #define FG_BLUE       ( FOREGROUND_BLUE | FOREGROUND_INTENSITY )
  #define FG_BROWN      ( FOREGROUND_RED | FOREGROUND_GREEN )
  #define FG_DARKBLUE   ( FOREGROUND_BLUE )
  #define FG_DARKGREY   ( FOREGROUND_INTENSITY )
  #define FG_GREEN      ( FOREGROUND_GREEN )
  #define FG_GREY       ( FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE )
  #define FG_LIGHTBLUE  ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY )
  #define FG_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY )
  #define FG_PINK       ( FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY )
  #define FG_RED        ( FOREGROUND_RED )
  #define FG_LIGHTRED   ( FOREGROUND_RED | FOREGROUND_INTENSITY )
  #define FG_TURQUOISE  ( FOREGROUND_BLUE | FOREGROUND_GREEN )
  #define FG_VIOLET     ( FOREGROUND_RED | FOREGROUND_BLUE )
  #define FG_WHITE      ( FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY )
  #define FG_YELLOW     ( FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY )

  // background colours
  #define BG_BLACK      ( 0 )
  #define BG_BLUE       ( BACKGROUND_BLUE | BACKGROUND_INTENSITY )
  #define BG_BROWN      ( BACKGROUND_RED | BACKGROUND_GREEN )
  #define BG_DARKBLUE   ( BACKGROUND_BLUE )
  #define BG_DARKGREY   ( BACKGROUND_INTENSITY )
  #define BG_GREEN      ( BACKGROUND_GREEN )
  #define BG_GREY       ( BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE )
  #define BG_LIGHTBLUE  ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY )
  #define BG_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY )
  #define BG_PINK       ( BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY )
  #define BG_RED        ( BACKGROUND_RED )
  #define BG_LIGHTRED   ( BACKGROUND_RED | BACKGROUND_INTENSITY )
  #define BG_TURQUOISE  ( BACKGROUND_BLUE | BACKGROUND_GREEN )
  #define BG_VIOLET     ( BACKGROUND_RED | BACKGROUND_BLUE )
  #define BG_WHITE      ( BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY )
  #define BG_YELLOW     ( BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY )

#elif defined(__APPLE__) || defined(__unix__)

#endif // OS



/**
  \fn void Error(const char *format, ...)

  \param[in] code    return code of application to commandline
  \param[in] pause   wait for keyboard input before terminating

  Display error message and terminate program. Output format is identical to
  printf(). Prior to program termination query for \<return\> unless
  background operation is specified.
*/
void Error(const wchar_t *format, ...)
{
  va_list vargs;
  va_start(vargs, format);
  setConsoleColor(PRM_COLOR_RED);
  fwprintf(stderr, L"Error: ");
  vfwprintf(stderr, format, vargs);
  va_end(vargs);
  fwprintf(stderr, L"\n");
  // Exit(1, 1);
}



/**
  \fn void Exit(uint8_t code, uint8_t pause)

  \param[in] code    return code of application to commandline
  \param[in] pause   wait for keyboard input before terminating

  Terminate program. Replace standard exit() to query for \<return\>
  before termination, unless background operation is specified.
*/
void Exit(uint8_t code, uint8_t pause) {

  // on error code !=0 ring bell
  if (code) {
    printf("\a");
    fflush(stdout);
  }

  // reset text color to default
  setConsoleColor(PRM_COLOR_DEFAULT);

  // optionally prompt for <return>
  if ((pause) && (!g_backgroundOperation)) {
    printf("\npress <return> to exit");
    fflush(stdout);
    fflush(stdin);
    getchar();
  }
  printf("\n");

  // terminate application
  exit(code);

} // Exit



/**
  \fn void setConsoleColor(uint8_t color)

  \param[in] color  new text color

  switch text color in console output to specified value
    Windows: uses Windows API functions
    POSIX:   uses VT100 escape codes
*/
void setConsoleColor(uint8_t color) {

  // for background operation skip to avoid strange control characters
  if (g_backgroundOperation)
    return;


#if defined(WIN32) || defined(WIN64)

  static WORD                   oldColor, colorBck;
  static char                   flag=0;
  CONSOLE_SCREEN_BUFFER_INFO    csbiInfo;

  // at first call get and store current text and backgound color
  if (flag==0) {
    flag = 1;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbiInfo);
    oldColor = csbiInfo.wAttributes;
    colorBck = (csbiInfo.wAttributes) & (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY);
  }

  // set to text color
  switch (color) {

    // revert color to start value
    case PRM_COLOR_DEFAULT:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), oldColor);
      break;

    // set color to black; retain background color
    case PRM_COLOR_BLACK:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_BLACK | colorBck);
      break;

    // set color to blue; retain background color
    case PRM_COLOR_BLUE:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_BLUE | colorBck);
      break;

    // set color to green; retain background color
    case PRM_COLOR_GREEN:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_GREEN | colorBck);
      break;

    // set color to red; retain background color
    case PRM_COLOR_RED:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_LIGHTRED | colorBck);
      break;

    // set color to pink; retain background color
    case PRM_COLOR_PINK:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_PINK | colorBck);
      break;

    // set color to white; retain background color
    case PRM_COLOR_WHITE:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_WHITE | colorBck);
      break;

    // set color to yellow; retain background color
    case PRM_COLOR_YELLOW:
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FG_YELLOW | colorBck);
      break;

    // else revert color to default
    default:
      //fprintf(stderr, "\n\ndefault\n\n");
      SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), oldColor);

  } //  switch (color)


#elif defined(__APPLE__) || defined(__unix__)

  // set to text color (see http://linuxgazette.net/issue65/padala.html)
  switch (color) {

    // revert color to start value
    case PRM_COLOR_DEFAULT:
      printf("\033[0m");
      //printf("\n\nrevert\n\n");
      fflush(stdout);
      break;

    // set color to black; retain background color
    case PRM_COLOR_BLACK:
      printf("\033[30m");
      //printf("\n\nblack\n\n");
      fflush(stdout);
      break;

    // set color to blue; retain background color
    case PRM_COLOR_BLUE:
      printf("\033[1;34m");
      //printf("\n\nblue\n\n");
      fflush(stdout);
      break;

    // set color to green; retain background color
    case PRM_COLOR_GREEN:
      printf("\033[1;32m");
      //printf("\n\ngreen\n\n");
      fflush(stdout);
      break;

    // set color to red; retain background color
    case PRM_COLOR_RED:
      printf("\033[1;31m");
      //printf("\n\nred\n\n");
      fflush(stdout);
      break;

    // set color to pink; retain background color
    case PRM_COLOR_PINK:
      printf("\033[1;35m");
      //printf("\n\npink\n\n");
      fflush(stdout);
      break;

    // set color to white; retain background color
    case PRM_COLOR_WHITE:
      printf("\033[37m");
      //printf("\n\nwhite\n\n");
      fflush(stdout);
      break;

    // set color to yellow; retain background color
    case PRM_COLOR_YELLOW:
      printf("\033[1;33m");
      //printf("\n\nyellow\n\n");
      fflush(stdout);
      break;

    // else revert color to default
    default:
      printf("\033[0m");
      //printf("\n\ndefault\n\n");
      fflush(stdout);

  } //  switch (color)

#else
  #error unknown OS type
#endif

} // setConsoleColor



/**
  \fn bool isDecString(const char *str)

  \param str     C-string to check (ends with '\0')

  \return check result

  Check if a string is a valid decimal number with only 0-9
*/
bool isDecString(const char *str) {

  // assert that string characters are in 0-9
  for (size_t i = 0; i < strlen(str); i++) {
    if (!isdigit(str[i])) {
      return false;
    }
  }
   
  return true;
  
} // isDecString()



/**
  \fn bool isHexString(const char *str)

  \param str     C-string to check (starts with "0x" and ends with '\0')

  \return check result

  Check if a string is a valid hexadecimal number starting with "0x", else only 0-9,a-f,A-F
*/
bool isHexString(const char *str) {

  // assert that string starts with "0x"
  if ((str[0] != '0') || ((str[1] != 'x') && (str[1] != 'X')))
    return false;

  // assert remaining string characters are in 0-9,a-f,A-F
  for (size_t i = 2; i < strlen(str); i++) {
    if (!isxdigit(str[i])) {
      return false;
    }
  }
   
  return true;
  
} // isHexString()



/**
  \fn bool isLittleEndian(void)

  \return true: little-endian; false: big-endian

  Check if machine is little-endian (e.g. Intel, ARM) or big-endian (e.g. PowerPC, SPARC). Is required e.g. for CRC32 checksum calculation
*/
bool isLittleEndian(void) {

  // check storage order of bytes
  uint16_t value = 1;
  return (*((uint8_t*) &value) == 1);
  
} // isLittleEndian()

// end of file
